/***************************************************************************
 *   Copyright (C) 2011 by The rPem3d developers                           *
 *   elseifkk@users.sf.net                                                 *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "config.h"

#include <sys/shm.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/param.h>
#include <sys/sysctl.h>

#include <sys/types.h>
#include <unistd.h>

key_t shmkey=0;
int shmid=0;
size_t hdrsz=0;
size_t shmoff=0;
unsigned char *pshm=0;
unsigned char *shmdata=0;
size_t shmsz=0;
size_t datasz=0;
size_t dataszm;

int pid2name ( int pid, char *pathname ) {
    #if defined(__FreeBSD__) && (__FreeBSD__ >= 7)
    int error, name[4];
    size_t len=1024;
    name[0] = CTL_KERN;
    name[1] = KERN_PROC;
    name[2] = KERN_PROC_PATHNAME;
    name[3] = pid;
    error = sysctl ( name, 4, pathname, &len, NULL, 0 );
    if ( error < 0 || len<=1 ) {
        perror ( "sysctl" );
        return 0;
    }
    return 1;
    #else
    return 0; // error
    #endif
}

void get_shm ( void *code, const size_t addr, size_t len) {
    if ( shmdata==0 ) return;
    unsigned char *p=shmdata;
    p+=addr;
    memcpy ( code,(const void *)p, len );
    return;
}

size_t attach_shm(int id, size_t off) {
    #if defined(__FreeBSD__) && (__FreeBSD__ >= 7)
    /* attach the segment to data space. */
    pshm = ( unsigned char * ) shmat ( id, ( void * ) 0, 0 );
    if ( pshm == ( unsigned char * )-1 ) {
        perror ( "shmat" );
        return 0;
    }
    shmoff=off;
    shmdata=pshm+off;//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    shmid=id;
    memcpy(&shmsz,shmdata,8);
    memcpy(&hdrsz,shmdata+8,8); //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    datasz=shmsz-hdrsz;//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    dataszm=datasz-1;
    return shmsz;
    #else
    return 0;
    #endif
}
int get_shmid() {
    return shmid;
}
size_t open_shm ( const char *pathname, int uid, size_t off ) {
    #if defined(__FreeBSD__) && (__FreeBSD__ >= 7)
    if ( ( shmkey = ftok ( pathname, uid ) ) == -1 ) {
        perror ( "ftok" );
        return 0;
    }
    if ((shmid = shmget ( shmkey, 0, 0))==-1) return 0;
    return attach_shm(shmid,off);
    #else
    return 0; // error
    #endif
}
int close_shm ( void ) {
    #if defined(__FreeBSD__) && (__FreeBSD__ >= 7)
    if ( pshm==0 ) return 0;
    if ( shmdt ( pshm ) ==-1 ) {
        perror ( "shmdt" );
        return 1;
    } else {
        /* Destroy the segment */
        if ( shmctl ( shmid, IPC_RMID, 0 ) == -1 ) {
            perror ( "shmctl" );
            return 1;
        }
        return 0;
    }
    #else
    return 1; // error
    #endif
}

int detach_shm() {
    #if defined(__FreeBSD__) && (__FreeBSD__ >= 7)
    if (pshm==0) return 0;
    int rc=shmdt(pshm);
    if (rc==-1) {
        perror("shmdt");
    } else {
        pshm=0;
        shmdata=0;
        shmsz=0;
        datasz=0;
        shmoff=0;
    }
    return rc;
    #else
    return 1; // error
    #endif
}
int *get_firstdata() {
    return (int *)(shmdata+hdrsz);
}

int *get_enddata() {
    return (int *)(shmdata+shmsz);
}
unsigned char *get_data(size_t pos) {
    size_t npos=pos&dataszm;
    unsigned char *p=shmdata;
     p+=hdrsz;
     p+=npos;
    return p;
}

void * new_shm(size_t s, int *exist, int *id) {
    char path[1024];
    if (pid2name(getpid(),path)==0) {
        strcpy(path,"./");
    }
    key_t oshmkey=ftok(path,0);
    int oshmflag=0666;
    int oshmid=shmget(oshmkey,s,oshmflag);
    if (oshmid<0) {
        oshmid=shmget(oshmkey,s,oshmflag|IPC_CREAT);
        *exist=0;
    } else {
        *exist=1;
    }
    *id=oshmid;
    if (oshmid<0) return NULL;
    return shmat ( oshmid, ( void * ) 0, 0 );
}

void del_shm(void *p, int id)
{
if(shmdt(p)!=0){
perror ("shmdt");
return;
}
if ( shmctl ( id, IPC_RMID, 0 ) == -1 ) {
            perror ( "shmctl" );
            return;
}
}
